package com.example.ytyproject.config;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.Arrays;

@Aspect
@Component
@Slf4j
public class LogUtil {

    @Pointcut("@annotation(com.example.ytyproject.config.AddLog)")
    public void pointcut() {
    }
    //前置通过
    @Before("pointcut()")
    public static void before(JoinPoint joinPoint) {
        //获取方法名
        String methodName = joinPoint.getSignature().getName();
        log.info("前置通知，你访问的方法为：{}", methodName);
    }

    //后置通知
    @After("pointcut()")
    public static void after(JoinPoint joinPoint) {
        System.out.println("方法运行后");
        log.info("后置通知，方法运行后，插入数据");


    }

    private void saveLog(ProceedingJoinPoint jp,long time) throws NoSuchMethodException, SecurityException, JsonProcessingException {
        //1.获取用户行为信息
        //1.1获取目标方法对象
        Method targetMethod=getTargetMethod(jp);
        //1.2获取目标方法的方法名信息
        String targetMethodName=
                targetMethod.getDeclaringClass().getName()+"."+targetMethod.getName();
        //1.3获取目标方法上的操作名
        String operation = getOperation(targetMethod);
        //1.4 目标方法参数(转换为字符串)
        //String params=Arrays.toString(jp.getArgs());
        String params=
                //将参数对象转换为json格式字符串
                new ObjectMapper().writeValueAsString(jp.getArgs());
        //2.封装用户行为信息
//        SysLog log=new SysLog();
//        log.setIp(IPUtils.getIpAddr());
//        log.setUsername(ShiroUtils.getUsername());//后续做完登陆以后，为登陆用户名
//        log.setOperation(operation);
//        log.setMethod(targetMethodName);
//        log.setParams(params);
//        log.setTime(time);
//        log.setCreatedTime(new Date());
//        //3.保存用户行为信息
//        sysLogService.saveObject(log);
        System.out.println("---------");
    }

    private Method getTargetMethod(ProceedingJoinPoint jp) throws NoSuchMethodException, SecurityException {
        Class<?> targetCls=jp.getTarget().getClass();
        MethodSignature ms=(MethodSignature)jp.getSignature();
        //获取目标方法
        Method targetMethod=//目标方法(类全名+方法名)
                targetCls.getDeclaredMethod(ms.getName(),ms.getParameterTypes());
        return targetMethod;
    }

    private String getOperation(Method targetMethod) {
        String operation="operation";
        AddLog requiredLog=
                targetMethod.getAnnotation(AddLog.class);
        if(requiredLog!=null) {
            operation=requiredLog.value();
        }
        return operation;
    }

    //后置异常通知
    @AfterThrowing(value = "execution(* com.yty.demo.service..*.*(..))",
            throwing = "ex")
    public static void afterException(Exception ex) {
        //捕获异常栈信息
        StringWriter sw = new StringWriter();
        ex.printStackTrace(new PrintWriter(sw, true));
    }
    //后置返回通知
    @AfterReturning(value = "execution(* com.yty.demo.service..*.*(..))",
            returning = "returnValue")
    public static void afterEnd(Object returnValue) {
        System.out.println("方法结束，返回值是：" + returnValue);
    }
    //环绕通知
    @Around(value = "pointcut()")
    public Object arround(ProceedingJoinPoint joinPoint) {
        //获取方法名
        String methodName = joinPoint.getSignature().getName();
        //获取参数
        Object[] args = joinPoint.getArgs();
        Object returnValue = null;
        try {
            System.out.println("前置通知：" + methodName + "方法执行，参数：" + Arrays.asList(args));
            joinPoint.proceed();
            System.out.println("后置通知：" + methodName + "方法执行，参数：" + Arrays.asList(args));
            saveLog(joinPoint, 1000L);
        } catch (Throwable e) {
            log.error("异常通知：", e);
        } finally {
            System.out.println("返回通知：" + returnValue);
        }
        return returnValue;
    }
}
